
package org.apache.solr.request;

import java.io.Closeable;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.core.SolrCore;
import org.apache.solr.handler.component.ResponseBuilder;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.util.TimeZoneUtils;

public class SolrRequestInfo {

    protected final static ThreadLocal<SolrRequestInfo> threadLocal = new ThreadLocal<SolrRequestInfo>();
    protected SolrQueryRequest req;
    protected SolrQueryResponse rsp;
    protected Date now;
    protected TimeZone tz;
    protected ResponseBuilder rb;
    protected List<Closeable> closeHooks;

    public static SolrRequestInfo getRequestInfo() {
        return threadLocal.get();
    }

    public static void setRequestInfo(SolrRequestInfo info) {
        // TODO: temporary sanity check... this can be changed to just an assert in the future
        SolrRequestInfo prev = threadLocal.get();
        if(prev != null) {
            SolrCore.log.error("Previous SolrRequestInfo was not closed!  req=" + prev.req.getOriginalParams().toString());
        }
        assert prev == null;

        threadLocal.set(info);
    }

    public static void clearRequestInfo() {

        try {
            SolrRequestInfo info = threadLocal.get();
            if(info != null && info.closeHooks != null) {
                for(Closeable hook : info.closeHooks) {
                    try {
                        hook.close();
                    }
                    catch(Throwable throwable) {
                        SolrException.log(SolrCore.log, "Exception during close hook", throwable);
                    }
                }
            }
        }
        finally {
            threadLocal.remove();
        }
    }

    public SolrRequestInfo(SolrQueryRequest req, SolrQueryResponse rsp) {
        this.req = req;
        this.rsp = rsp;
    }

    public Date getNOW() {

        if(now != null) {
            return now;
        }

        String nowStr = req.getParams().get(CommonParams.NOW);
        long ms = (nowStr != null) ? Long.parseLong(nowStr) : req.getStartTime();

        now = new Date(ms);
        return now;
    }

    /**
     * The TimeZone specified by the request, or null if none was specified
     */
    public TimeZone getClientTimeZone() {

        if(tz == null) {
            String tzStr = req.getParams().get(CommonParams.TZ);
            if(tzStr != null) {
                tz = TimeZoneUtils.getTimeZone(tzStr);
                if(null == tz) {
                    throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Solr JVM does not support TZ: " + tzStr);
                }
            }
        }
        return tz;
    }

    public SolrQueryRequest getReq() {
        return req;
    }

    public SolrQueryResponse getRsp() {
        return rsp;
    }

    /**
     * May return null if the request handler is not based on SearchHandler
     */
    public ResponseBuilder getResponseBuilder() {
        return rb;
    }

    public void setResponseBuilder(ResponseBuilder rb) {
        this.rb = rb;
    }

    public void addCloseHook(Closeable hook) {
        // is this better here, or on SolrQueryRequest?
        synchronized(this) {
            if(closeHooks == null) {
                closeHooks = new LinkedList<>();
            }
            closeHooks.add(hook);
        }
    }
}
